home *** CD-ROM | disk | FTP | other *** search
/ Visual Cafe 3 / Visual Cafe 3.ISO / Vcafe / JFC.bin / BasicTreeUI.java < prev    next >
Text File  |  1998-06-30  |  67KB  |  2,253 lines

  1. /*
  2.  * @(#)BasicTreeUI.java    1.79 98/03/05
  3.  * 
  4.  * Copyright (c) 1997 Sun Microsystems, Inc. All Rights Reserved.
  5.  * 
  6.  * This software is the confidential and proprietary information of Sun
  7.  * Microsystems, Inc. ("Confidential Information").  You shall not
  8.  * disclose such Confidential Information and shall use it only in
  9.  * accordance with the terms of the license agreement you entered into
  10.  * with Sun.
  11.  * 
  12.  * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
  13.  * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  14.  * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
  15.  * PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
  16.  * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
  17.  * THIS SOFTWARE OR ITS DERIVATIVES.
  18.  * 
  19.  */
  20.  
  21. package com.sun.java.swing.plaf.basic;
  22.  
  23. import com.sun.java.swing.*;
  24. import com.sun.java.swing.event.*;
  25. import com.sun.java.swing.text.DefaultTextUI;
  26. import java.awt.*;
  27. import java.awt.event.*;
  28. import java.beans.*;
  29. import java.io.*;
  30. import java.util.*;
  31. import com.sun.java.swing.plaf.ComponentUI;
  32. import com.sun.java.swing.plaf.UIResource;
  33. import com.sun.java.swing.plaf.TreeUI;
  34. import com.sun.java.swing.tree.*;
  35.  
  36. /**
  37.  * The basic L&F for a hierarchical data structure.
  38.  * <p>
  39.  * Warning: serialized objects of this class will not be compatible with
  40.  * future swing releases.  The current serialization support is appropriate
  41.  * for short term storage or RMI between Swing1.0 applications.  It will
  42.  * not be possible to load serialized Swing1.0 objects with future releases
  43.  * of Swing.  The JDK1.2 release of Swing will be the compatibility
  44.  * baseline for the serialized form of Swing objects.
  45.  *
  46.  * @version 1.79 03/05/98
  47.  * @author Rob Davis
  48.  * @author Ray Ryan
  49.  * @author Scott Violet
  50.  */
  51.  
  52. public class BasicTreeUI extends AbstractTreeUI implements CellEditorListener,
  53.           FocusListener, KeyListener, MouseListener,
  54.           PropertyChangeListener
  55. {
  56.     static private final int LEFT_CHILD_INDENT = 7;
  57.     static private final int RIGHT_CHILD_INDENT = 13;
  58.     static private final Insets EMPTY_INSETS = new Insets(0, 0, 0, 0);
  59.  
  60.     transient protected Icon        collapsedIcon;
  61.     transient protected Icon        expandedIcon;
  62.  
  63.     /** Color used to draw hash marks.  If null no hash marks will be
  64.       * drawn. */
  65.     private Color hashColor;
  66.  
  67.     /** Distance between left margin and where verical dashes will be
  68.       * drawn. */
  69.     protected int               leftChildIndent;
  70.     /** Distance to add to leftChildIndent to determine where cell
  71.       * contents will be drawn. */
  72.     protected int               rightChildIndent;
  73.     /** Total distance that will be indented.  The sum of leftChildIndent
  74.       * and rightChildIndent. */
  75.     protected int               totalChildIndent;
  76.  
  77.     /** Minimum preferred size. */
  78.     protected Dimension         preferredMinSize;
  79.  
  80.     /** Index of the row that was last selected. */
  81.     protected int                lastSelectedRow;
  82.  
  83.     /** Component that we're going to be drawing into. */
  84.     protected JTree              tree;
  85.  
  86.     /** Renderer that is being used to do the actual cell drawing. */
  87.     transient protected TreeCellRenderer   currentCellRenderer;
  88.  
  89.     /** Set to true if the renderer that is currently in the tree was
  90.      * created by this instance. */
  91.     protected boolean            createdRenderer;
  92.  
  93.     /** Editor for the tree. */
  94.     transient protected TreeCellEditor     cellEditor;
  95.  
  96.     /** Set to true if editor renderer that is currently in the tree was
  97.      * created by this instance. */
  98.     protected boolean            createdCellEditor;
  99.  
  100.     /** When editing, this will be the Component that is doing the actual
  101.       * editing. */
  102.     protected Component          editingComponent;
  103.  
  104.     /** Path that is being edited. */
  105.     protected TreePath           editingPath;
  106.  
  107.     /** Key code that is being generated for. */
  108.     protected Action             repeatKeyAction;
  109.  
  110.     /** Set to true while keyPressed is active. */
  111.     protected boolean            isKeyDown;
  112.  
  113.     /** Set to false when editing and shouldSelectCell() returns true meaning
  114.       * the node should be selected before editing, used in completeEditing. */
  115.     protected boolean            stopEditingInCompleteEditing;
  116.  
  117.     /** Used to paint the TreeCellRenderer. */
  118.     protected CellRendererPane   rendererPane;
  119.  
  120.     /** Size needed to completely display all the nodes. */
  121.     protected Dimension          cPreferredSize;
  122.  
  123.     /** Is the preferredSize valid? */
  124.     protected boolean            validCachedPreferredSize;
  125.  
  126.     /** Used for large models, listens for moved/resized events and
  127.      * updates the validCachedPreferredSize bit accordingly. */
  128.     transient protected ComponentAdapter   componentListener;
  129.  
  130.     /** Used for minimizing the drawing of vertical lines. */
  131.     protected Vector             drawingCache;
  132.  
  133.     // Cached listeners
  134.     private PropertyChangeListener propertyChangeListener;
  135.     private MouseListener mouseListener;
  136.     private FocusListener focusListener;
  137.     private KeyListener keyListener;
  138.  
  139.  
  140.     public static ComponentUI createUI(JComponent x) {
  141.     return new BasicTreeUI();
  142.     }
  143.  
  144.     public BasicTreeUI()
  145.     {
  146.     super();
  147.     }
  148.  
  149.     protected Color getHashColor()
  150.     {
  151.         return hashColor;
  152.     }
  153.  
  154.     protected void setHashColor( Color color )
  155.     {
  156.         hashColor = color;
  157.     }
  158.  
  159.     public void setLeftChildIndent(int newAmount)
  160.     {
  161.     leftChildIndent = newAmount;
  162.     totalChildIndent = leftChildIndent + rightChildIndent;
  163.     }
  164.  
  165.     public int getLeftChildIndent()
  166.     {
  167.     return leftChildIndent;
  168.     }
  169.  
  170.     public void setRightChildIndent(int newAmount)
  171.     {
  172.     rightChildIndent = newAmount;
  173.     totalChildIndent = leftChildIndent + rightChildIndent;
  174.     }
  175.  
  176.     public int getRightChildIndent()
  177.     {
  178.     return rightChildIndent;
  179.     }
  180.  
  181.     public void setExpandedIcon(Icon newG)
  182.     {
  183.     expandedIcon = newG;
  184.     }
  185.  
  186.     public Icon getExpandedIcon()
  187.     {
  188.     return expandedIcon;
  189.     }
  190.  
  191.     public void setCollapsedIcon(Icon newG)
  192.     {
  193.     collapsedIcon = newG;
  194.     }
  195.  
  196.     public Icon getCollapsedIcon()
  197.     {
  198.     return collapsedIcon;
  199.     }
  200.  
  201.     /**
  202.      * Updates the componentListener, if necessary.
  203.      */
  204.     public void setLargeModel(boolean largeModel) {
  205.     if(largeModel && tree != null && componentListener == null) {
  206.         componentListener = new ComponentAdapter() {
  207.         public void componentMoved(ComponentEvent e) {
  208.             validCachedPreferredSize = false;
  209.         }
  210.         };
  211.         tree.addComponentListener(componentListener);
  212.     }
  213.     else if(!largeModel && tree != null && componentListener != null) {
  214.         tree.removeComponentListener(componentListener);
  215.         componentListener = null;
  216.     }
  217.     super.setLargeModel(largeModel);
  218.     }
  219.  
  220.     public void installUI(JComponent c) {
  221.         if ( c == null ) {
  222.         throw new NullPointerException( "null component passed to BasicTreeUI.installUI()" );
  223.         }
  224.  
  225.     tree = (JTree)c;
  226.  
  227.     drawingCache = new Vector();
  228.  
  229.     // Data member initializations
  230.     stopEditingInCompleteEditing = true;
  231.     lastSelectedRow = -1;
  232.     setLeftChildIndent(LEFT_CHILD_INDENT);
  233.     setRightChildIndent(RIGHT_CHILD_INDENT);
  234.     cPreferredSize = new Dimension();
  235.  
  236.     // Subcomponents
  237.     if ( (rendererPane = createCellRendererPane( c )) != null ) {
  238.         tree.add( rendererPane );
  239.     }
  240.  
  241.     // Boilerplate install block
  242.     installDefaults( c );
  243.     installListeners( c );
  244.     installKeyboardActions( c );
  245.  
  246.     // Custom install code
  247.     this.setRowHeight(tree.getRowHeight());
  248.     this.setRootVisible(tree.isRootVisible());
  249.     this.setShowsRootHandles(tree.getShowsRootHandles());
  250.     this.setModel(tree.getModel());
  251.     this.setSelectionModel(tree.getSelectionModel());
  252.     this.setLargeModel(tree.isLargeModel());
  253.  
  254.     updateRenderer();
  255.  
  256.     validCachedPreferredSize = false;
  257.     }
  258.  
  259.     protected void installDefaults( JComponent c ) {
  260.     if(c.getBackground() == null || c.getBackground() instanceof UIResource) {
  261.         c.setBackground(UIManager.getColor("Tree.background"));
  262.     } 
  263.     if(getHashColor() == null || getHashColor() instanceof UIResource) {
  264.         setHashColor(UIManager.getColor("Tree.hash"));
  265.     }
  266.     if (c.getFont() == null || c.getFont() instanceof UIResource)
  267.         c.setFont( UIManager.getFont("Tree.font") );
  268.  
  269.     setExpandedIcon( (Icon)UIManager.get( "Tree.expandedIcon" ) );
  270.     setCollapsedIcon( (Icon)UIManager.get( "Tree.collapsedIcon" ) );
  271.     }
  272.  
  273.     protected void installListeners( JComponent c ) {
  274.         if ( (propertyChangeListener = createPropertyChangeListener( c )) != null ) {
  275.         c.addPropertyChangeListener( propertyChangeListener );
  276.     }
  277.         if ( (mouseListener = createMouseListener( c )) != null ) {
  278.         c.addMouseListener( mouseListener );
  279.     }
  280.         if ( (focusListener = createFocusListener( c )) != null ) {
  281.         c.addFocusListener( focusListener );
  282.     }
  283.         if ( (keyListener = createKeyListener( c )) != null ) {
  284.         c.addKeyListener( keyListener );
  285.     }
  286.     }
  287.  
  288.     protected PropertyChangeListener createPropertyChangeListener( JComponent c ) {
  289.         return this;
  290.     }
  291.  
  292.     protected MouseListener createMouseListener( JComponent c ) {
  293.         return this;
  294.     }
  295.  
  296.     protected FocusListener createFocusListener( JComponent c ) {
  297.         return this;
  298.     }
  299.  
  300.     protected KeyListener createKeyListener( JComponent c ) {
  301.         return this;
  302.     }
  303.  
  304.     protected CellRendererPane createCellRendererPane( JComponent c ) {
  305.         return new CellRendererPane();
  306.     }
  307.  
  308.     public void uninstallUI(JComponent c) {
  309.     completeEditing();
  310.  
  311.     tree.remove(rendererPane);
  312.  
  313.     uninstallDefaults( c );
  314.     uninstallListeners( c );
  315.     uninstallKeyboardActions( c );
  316.  
  317.     checkConsistency();
  318.  
  319.     // These should be the last items in this method.  DON'T add after this.
  320.     if(createdRenderer) {
  321.         tree.setCellRenderer(null);
  322.     }
  323.     if(createdCellEditor) {
  324.         tree.setCellEditor(null);
  325.     }
  326.     rendererPane = null;
  327.         componentListener = null;
  328.     propertyChangeListener = null;
  329.     mouseListener = null;
  330.     focusListener = null;
  331.     keyListener = null;
  332.     setSelectionModel(null);
  333.     setModel(null);
  334.     tree = null;
  335.     drawingCache = null;
  336.     }
  337.  
  338.     protected void uninstallDefaults( JComponent c ) {
  339.     }
  340.  
  341.     protected void uninstallListeners( JComponent c ) {
  342.     if(componentListener != null) {
  343.         tree.removeComponentListener(componentListener);
  344.     }
  345.         if ( propertyChangeListener != null ) {
  346.         c.removePropertyChangeListener( propertyChangeListener );
  347.     }
  348.         if ( mouseListener!= null ) {
  349.         c.removeMouseListener( mouseListener );
  350.     }
  351.         if ( focusListener!= null ) {
  352.         c.removeFocusListener( focusListener );
  353.     }
  354.         if ( keyListener!= null ) {
  355.         c.removeKeyListener( keyListener );
  356.     }
  357.     }
  358.  
  359.     /**
  360.       * Based on the value has changed will message the appropriate
  361.       * method.  Which is one of treeRendererChanged, setModel,
  362.       * setRootVisible, setShowsRootHandles, or setRowHeight.
  363.       */
  364.     public void propertyChange(PropertyChangeEvent event) {
  365.     if(event.getSource() == tree) {
  366.         String              changeName = event.getPropertyName();
  367.  
  368.         completeEditing();
  369.         if(changeName.equals(JTree.CELL_RENDERER_PROPERTY)) {
  370.         updateRenderer();
  371.         tree.repaint();
  372.         }
  373.         else if(changeName.equals(JTree.TREE_MODEL_PROPERTY))
  374.         setModel((TreeModel)event.getNewValue());
  375.         else if(changeName.equals(JTree.ROOT_VISIBLE_PROPERTY)) {
  376.         setRootVisible(((Boolean)event.getNewValue()).
  377.                     booleanValue());
  378.         tree.repaint();
  379.         }
  380.         else if(changeName.equals(JTree.SHOWS_ROOT_HANDLES_PROPERTY)) {
  381.         setShowsRootHandles(((Boolean)event.getNewValue()).
  382.                     booleanValue());
  383.         tree.repaint();
  384.         }
  385.         else if(changeName.equals(JTree.ROW_HEIGHT_PROPERTY))
  386.         setRowHeight(((Integer)event.getNewValue()).
  387.                  intValue());
  388.         else if(changeName.equals(JTree.CELL_EDITOR_PROPERTY)) {
  389.         updateCellEditor();
  390.         }
  391.         else if(changeName.equals(JTree.EDITABLE_PROPERTY)) {
  392.         updateCellEditor();
  393.         }
  394.         else if(changeName.equals(JTree.LARGE_MODEL_PROPERTY)) {
  395.         setLargeModel(tree.isLargeModel());
  396.         }
  397.         else if(changeName.equals(JTree.SELECTION_MODEL_PROPERTY)) {
  398.         setSelectionModel(tree.getSelectionModel());
  399.         }
  400.     }
  401.     else if(event.getSource() == treeSelectionModel)
  402.         treeSelectionModel.resetRowSelection();
  403.     }
  404.  
  405.     protected void installKeyboardActions( JComponent c )
  406.     {
  407.     c.registerKeyboardAction(new TreeIncrementAction(-1, "UP"),
  408.                  KeyStroke.getKeyStroke(KeyEvent.VK_UP,0),
  409.                  JComponent.WHEN_FOCUSED);
  410.     c.registerKeyboardAction(new TreeIncrementAction(1, "DOWN"),
  411.                  KeyStroke.getKeyStroke(KeyEvent.VK_DOWN,0),
  412.                  JComponent.WHEN_FOCUSED);
  413.     c.registerKeyboardAction(new TreeTraverseAction(1, "RIGHT"),
  414.                  KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT,0),
  415.                  JComponent.WHEN_FOCUSED);
  416.     c.registerKeyboardAction(new TreeTraverseAction(-1, "LEFT"),
  417.                  KeyStroke.getKeyStroke(KeyEvent.VK_LEFT,0),
  418.                  JComponent.WHEN_FOCUSED);
  419.     c.registerKeyboardAction(new TreePageAction(-1, "P_UP"),
  420.                  KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_UP,0),
  421.                  JComponent.WHEN_FOCUSED);
  422.     c.registerKeyboardAction(new TreePageAction(1, "P_DOWN"),
  423.                  KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_DOWN,0),
  424.                  JComponent.WHEN_FOCUSED);
  425.     c.registerKeyboardAction(new TreeHomeAction(-1, "HOME"),
  426.                  KeyStroke.getKeyStroke(KeyEvent.VK_HOME,0),
  427.                  JComponent.WHEN_FOCUSED);
  428.     c.registerKeyboardAction(new TreeHomeAction(1, "END"),
  429.                  KeyStroke.getKeyStroke(KeyEvent.VK_END,0),
  430.                  JComponent.WHEN_FOCUSED);
  431.     c.registerKeyboardAction(new TreeToggleAction("TOGGLE"),
  432.                  KeyStroke.getKeyStroke(KeyEvent.VK_ENTER,0),
  433.                  JComponent.WHEN_FOCUSED);
  434.     }
  435.  
  436.     protected void uninstallKeyboardActions( JComponent c )
  437.     {
  438.     c.unregisterKeyboardAction(KeyStroke.getKeyStroke(KeyEvent.VK_UP,
  439.                               0));
  440.     c.unregisterKeyboardAction(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN,
  441.                               0));
  442.     c.unregisterKeyboardAction(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT,
  443.                               0));
  444.     c.unregisterKeyboardAction(KeyStroke.getKeyStroke
  445.                    (KeyEvent.VK_RIGHT, 0));
  446.     c.unregisterKeyboardAction(KeyStroke.getKeyStroke(KeyEvent.
  447.                               VK_PAGE_UP, 0));
  448.     c.unregisterKeyboardAction(KeyStroke.getKeyStroke
  449.                    (KeyEvent.VK_PAGE_DOWN, 0));
  450.     c.unregisterKeyboardAction(KeyStroke.getKeyStroke(KeyEvent.
  451.                               VK_HOME, 0));
  452.     c.unregisterKeyboardAction(KeyStroke.getKeyStroke
  453.                    (KeyEvent.VK_END, 0));
  454.     c.unregisterKeyboardAction(KeyStroke.getKeyStroke
  455.                    (KeyEvent.VK_ENTER, 0));
  456.     }
  457.  
  458.     /** 
  459.       * Updates the cellEditor based on the editability of the JTree that
  460.       * we're contained in.  If the tree is editable but doesn't have a
  461.       * cellEditor, a basic one will be used.
  462.       */
  463.     protected void updateCellEditor() {
  464.     TreeCellEditor        newEditor;
  465.  
  466.     completeEditing();
  467.     if(tree == null)
  468.         newEditor = null;
  469.     else {
  470.         if(tree.isEditable()) {
  471.         newEditor = tree.getCellEditor();
  472.         if(newEditor == null) {
  473.             newEditor = createDefaultCellEditor();
  474.             if(newEditor != null) {
  475.             tree.setCellEditor(newEditor);
  476.             createdCellEditor = true;
  477.             }
  478.         }
  479.         }
  480.         else
  481.         newEditor = null;
  482.     }
  483.     if(newEditor != cellEditor) {
  484.         if(cellEditor != null)
  485.         cellEditor.removeCellEditorListener(this);
  486.         cellEditor = newEditor;
  487.         if(newEditor != null)
  488.         newEditor.addCellEditorListener(this);
  489.         createdCellEditor = false;
  490.     }
  491.     }
  492.  
  493.     /**
  494.       * Messaged from the tree we're in when the renderer has changed.
  495.       * Updates the size if necessary.
  496.       */
  497.     protected void updateRenderer() {
  498.     TreeCellRenderer      newCellRenderer;
  499.  
  500.     newCellRenderer = tree.getCellRenderer();
  501.     if(newCellRenderer == null && tree != null) {
  502.         tree.setCellRenderer(createDefaultCellRenderer());
  503.         createdRenderer = true;
  504.     }
  505.     else
  506.     {
  507.         createdRenderer = false;
  508.         currentCellRenderer = newCellRenderer;
  509.         if(!largeModel)
  510.         this.updateNodeSizes(true);
  511.         if(createdCellEditor && tree != null) {
  512.         tree.setCellEditor(null);
  513.         }
  514.     }
  515.     updateCellEditor();
  516.     }
  517.  
  518.     /** Checks to insure that the all the sizes of the nodes are valid,
  519.       * and if there isn't a valid node size, as determined by
  520.       * updateNodeSizes in our superclass, than <b>updateNodeSizes()</b>
  521.       * is messaged.
  522.       */
  523.     public boolean checkConsistency()
  524.     {
  525.     if(tree != null && updateNodeSizes)
  526.     {
  527.         if(!largeModel)
  528.         this.updateNodeSizes(true);
  529.         return true;
  530.     }
  531.     return false;
  532.     }
  533.  
  534.     public void updateNodeSizes(boolean updateAll) {
  535.     super.updateNodeSizes(updateAll);
  536.     validCachedPreferredSize = false;
  537.     }
  538.  
  539.     /**
  540.       * Creates a default cell editor.
  541.       */
  542.     protected TreeCellEditor createDefaultCellEditor() {
  543.     if(currentCellRenderer != null &&
  544.        (currentCellRenderer instanceof BasicTreeCellRenderer))
  545.         return new BasicTreeCellEditor((BasicTreeCellRenderer)
  546.                      currentCellRenderer);
  547.     return new BasicTreeCellEditor(null);
  548.     }
  549.  
  550.     /**
  551.       * Returns the default cell renderer that is used to do the
  552.       * stamping of each node.
  553.       */
  554.     public TreeCellRenderer createDefaultCellRenderer() {
  555.     return new BasicTreeCellRenderer();
  556.     }
  557.  
  558.     /**
  559.       * Returns the x origin of the given node, which is based on whether
  560.       * or not we're showing handles and the visible level of the node
  561.       * multiplied by the right and left indent factor.
  562.       */
  563.     public int getXOriginOfNode(VisibleTreeNode node)
  564.     {
  565.     int                visibleLevel = node.getVisibleLevel();
  566.  
  567.     if(this.getShowsRootHandles())
  568.         visibleLevel++;
  569.     return (totalChildIndent * visibleLevel);
  570.     }
  571.  
  572.     /**
  573.       * Messages the tree to configure the cell, and returns the
  574.       * the size of the component.
  575.       */
  576.     public Dimension getSizeOfNode(VisibleTreeNode node, int index)
  577.     {
  578.       Component       aComponent = null;
  579.  
  580.     if(currentCellRenderer != null) {
  581.         aComponent = currentCellRenderer.getTreeCellRendererComponent
  582.         (tree, node.getValue(), false, node.isExpanded(),
  583.          node.isLeaf(), index, false);
  584.         if(tree != null) {
  585.         /* Notice how it is never removed.  This is OK according to
  586.            hans. */
  587.         rendererPane.add(aComponent);
  588.         aComponent.validate();
  589.         return aComponent.getPreferredSize();
  590.         }
  591.         return aComponent.getPreferredSize();
  592.     }
  593.     return new Dimension(0, 0);
  594.     }
  595.  
  596.     /**
  597.      * Returns the rectangle needed to draw the passed in row. This messages
  598.      * getLargeBoundsOf(parent, row, childUserObject, null, null) for the
  599.      * return value.
  600.      */
  601.     protected Rectangle getLargeBoundsOf(LargeTreeModelNode parent, int row,
  602.                      Object childUserObject) {
  603.     return getLargeBoundsOf(parent, row, childUserObject, null, null);
  604.     }
  605.  
  606.     /**
  607.      * Returns the rectangle needed to draw the passed in row. If Component
  608.      * is non-null the size is taken from the component. If placeIn is
  609.      * non-null the returned Rectangle will be placeIn[0].
  610.      */
  611.     protected Rectangle getLargeBoundsOf(LargeTreeModelNode parent, int row,
  612.                      Object childUserObject,
  613.                      Component component,
  614.                      Rectangle[] placeIn) {
  615.     Rectangle          retRectangle;
  616.     int                visibleLevel;
  617.  
  618.     if(placeIn == null)
  619.         retRectangle = new Rectangle();
  620.     else
  621.         retRectangle = placeIn[0];
  622.     if(parent == null)
  623.         visibleLevel = 0;
  624.     else {
  625.          visibleLevel = parent.getVisibleLevel() + 1;
  626.          if(this.getShowsRootHandles())
  627.          visibleLevel++;
  628.     }
  629.     retRectangle.x = (totalChildIndent * visibleLevel);
  630.     retRectangle.y = row * rowHeight;
  631.     retRectangle.height = rowHeight;
  632.     if(component == null) {
  633.         if(currentCellRenderer != null) {
  634.         Component   aComponent;
  635.  
  636.         aComponent = currentCellRenderer.getTreeCellRendererComponent
  637.             (tree, childUserObject, false, false, false, row, false);
  638.         if(tree != null) {
  639.             /* Notice how it is never removed.  This is OK according to
  640.                hans. */
  641.             rendererPane.add(aComponent);
  642.             aComponent.validate();
  643.         }
  644.         Dimension  size = aComponent.getPreferredSize();
  645.         retRectangle.width = size.width;
  646.         }
  647.         else
  648.         retRectangle.width = 0;
  649.     }
  650.     else {
  651.         Dimension      size = component.getPreferredSize();
  652.  
  653.         retRectangle.width = size.width;
  654.     }
  655.     return retRectangle;
  656.     }
  657.  
  658.     /**
  659.      * Updates the <code>cPreferredSize</code> instance variable,
  660.      * which is returned from <code>getPreferredSize()</code>.<p>
  661.      * If <code>largeModel</code> is true, the width is determined
  662.      * from only the visible rows, otherwise the width is determined
  663.      * from <code>getMaxNodeWidth</code>.
  664.      */
  665.     protected void updateCachedPreferredSize() {
  666.     if(!largeModel) {
  667.         int             rowCount = this.getRowCount();
  668.         int             suggestedHeight;
  669.  
  670.         if(rowCount > 0) {
  671.         if(!isFixedRowHeight()) {
  672.             VisibleTreeNode     lastNode = this.getNode(rowCount - 1);
  673.  
  674.             suggestedHeight = lastNode.getYOrigin() + 
  675.             lastNode.getPreferredSize().height;
  676.         }
  677.         else
  678.             suggestedHeight = this.getRowHeight() * rowCount;
  679.         }
  680.         else
  681.         suggestedHeight = 0;
  682.         cPreferredSize.height = suggestedHeight;
  683.         cPreferredSize.width = this.getMaxNodeWidth();
  684.     }
  685.     else if(tree != null) {
  686.         /* NOTE: The determinination of the row width could be made a
  687.            little snappier if made recusive... Just a little though. */
  688.         int                  firstRow, lastRow;
  689.         int                  minWidth = 0;
  690.         Rectangle            visRect;
  691.  
  692.         visRect = tree.getVisibleRect();
  693.         firstRow = getRowContainingYLocation(visRect.y);
  694.         lastRow = getRowContainingYLocation(visRect.y + visRect.height);
  695.         if(firstRow != -1 && firstRow != -1) {
  696.         Rectangle        rowBounds;
  697.  
  698.         while(firstRow <= lastRow) {
  699.             rowBounds = getRowBounds(firstRow);
  700.             if(rowBounds != null)
  701.             minWidth = Math.max(minWidth,
  702.                         rowBounds.x + rowBounds.width);
  703.             firstRow++;
  704.         }
  705.         }
  706.         cPreferredSize.width = minWidth;
  707.         cPreferredSize.height = getRowCount() * getRowHeight();
  708.     }
  709.     else
  710.         cPreferredSize.width = cPreferredSize.height = 0;
  711.     validCachedPreferredSize = true;
  712.     }
  713.  
  714.     /**
  715.       * Messaged whenever nodes are added/removed from the visible list,
  716.       * or the height/width of a node changes.  This updates the size
  717.       * of the JTree we're drawing for based on the dimension
  718.       * returned from <b>getPreferredSize</b>.
  719.       */
  720.     public void visibleNodesChanged() {
  721.     if(tree != null) {
  722.         validCachedPreferredSize = false;
  723.         tree.treeDidChange();
  724.     }
  725.     }
  726.  
  727.     /**
  728.       * Messaged from the VisibleTreeNode after it has been expanded.
  729.       */
  730.     protected void pathWasExpanded(TreePath path) {
  731.     if(tree != null) {
  732.         tree.fireTreeExpanded(path);
  733.     }
  734.     }
  735.  
  736.     /**
  737.       * Messaged from the VisibleTreeNode after it has collapsed.
  738.       */
  739.     protected void pathWasCollapsed(TreePath path) {
  740.     if(tree != null) {
  741.         tree.fireTreeCollapsed(path);
  742.     }
  743.     }
  744.  
  745.     /**
  746.       * Ensures that the rows identified by beginRow through endRow are
  747.       * visible.
  748.       */
  749.     public void ensureRowsAreVisible(int beginRow, int endRow) {
  750.     if(tree != null && beginRow >= 0 && endRow < getRowCount()) {
  751.         if(beginRow == endRow)
  752.         tree.scrollRectToVisible(getRowBounds(beginRow));
  753.         else {
  754.         Rectangle   beginRect = getRowBounds(beginRow);
  755.         Rectangle   testRect = beginRect;
  756.         int         beginY = beginRect.y;
  757.         int         maxX = beginRect.x + beginRect.width;
  758.         int         minX = beginRect.x;
  759.         int         availHeight = tree.getVisibleRect().height;
  760.  
  761.         for(int counter = beginRow + 1; counter < endRow; counter++) {
  762.             testRect = getRowBounds(counter);
  763.             if((testRect.width + testRect.x) > maxX)
  764.             maxX = testRect.width + testRect.x;
  765.             if(testRect.x < minX)
  766.             minX = testRect.x;
  767.             if((testRect.y + testRect.height - beginY) > availHeight)
  768.             counter = endRow;
  769.         }
  770.         tree.scrollRectToVisible(new Rectangle(minX, beginY,
  771.                           maxX - minX,
  772.                           testRect.y + testRect.height-
  773.                           beginY));
  774.         }
  775.     }
  776.     }
  777.  
  778.     /**
  779.       * Makes sure all the path components in path are expanded (accept
  780.       * for the last path component) and tries to scroll the resulting path
  781.       * to be visible (the scrolling will only work if the JTree is
  782.       * contained in a JScrollPane).
  783.       */
  784.     public void scrollPathToVisible(TreePath path) {
  785.     makeVisible(path);
  786.  
  787.     Rectangle            rect = getPathBounds(path);
  788.  
  789.     if(rect != null && tree != null)
  790.         tree.scrollRectToVisible(rect);
  791.     }
  792.  
  793.     /**
  794.       * Scrolls the item identified by row to be visible.  This will
  795.       * only work if the JTree is contained in a JSrollPane.
  796.       */
  797.     public void scrollRowToVisible(int row) {
  798.     Rectangle            rect = getRowBounds(row);
  799.  
  800.     if(rect != null && tree != null)
  801.         tree.scrollRectToVisible(rect);
  802.     }
  803.  
  804.     /**
  805.      * Return currentCellRenderer, which will either be the trees
  806.      * renderer, or defaultCellRenderer, which ever wasn't null.
  807.      * currentCellRenderer is set as part of checkConsistency().
  808.      */
  809.     public TreeCellRenderer getCellRenderer() {
  810.     return currentCellRenderer;
  811.     }
  812.  
  813.     /**
  814.      * Creates a new instance of BasicLargeTreeModelNode.
  815.      */
  816.     protected LargeTreeModelNode createExpandedNodeForValue(Object value,
  817.                               int childIndex) {
  818.     return new BasicLargeTreeModelNode(this, value, childIndex);
  819.     }
  820.  
  821.     public void paint(Graphics g, JComponent c) {
  822.     if (tree != c) {
  823.         throw new InternalError("incorrect component");
  824.     }
  825.  
  826.     checkConsistency();
  827.  
  828.     TreeCellRenderer renderer = getCellRenderer();
  829.     final int        totalIndent = totalChildIndent;
  830.  
  831.     Color            bColor = tree.getBackground();
  832.     Dimension        cSize = c.getSize();
  833.     Rectangle        paintBounds = g.getClipBounds();
  834.     int              beginRow, endRow;
  835.  
  836.     beginRow = getRowContainingYLocation(paintBounds.y);
  837.     endRow = getRowContainingYLocation(paintBounds.y + 
  838.                         paintBounds.height);
  839.  
  840.     boolean        shouldPaint = (beginRow > -1 && endRow > -1);
  841.  
  842.     if(shouldPaint && largeModel) {
  843.         int[]               rowCounter = new int[1];
  844.  
  845.         if(isRootVisible()) {
  846.         rowCounter[0] = 0;
  847.         ((BasicLargeTreeModelNode)largeRoot).paintAll
  848.             (new BasicTreeUIPaintInfo(this, g), rowCounter,
  849.              beginRow, endRow, 0, this);
  850.         }
  851.         else {
  852.         rowCounter[0] = -1;
  853.         ((BasicLargeTreeModelNode)largeRoot).paintAll
  854.             (new BasicTreeUIPaintInfo(this, g), rowCounter,
  855.              beginRow, endRow, -1, this);
  856.         }
  857.     }
  858.     else if(shouldPaint) {
  859.  
  860.         g.setColor( getHashColor() );
  861.         
  862.         // Draw the lines, knobs, and rows
  863.         VisibleTreeNode  childNode = getNode( beginRow );
  864.         VisibleTreeNode  parentNode = (VisibleTreeNode)childNode.getParent();
  865.  
  866.         // Find each parent and have them draw a line to their last child
  867.         while ( parentNode != null )
  868.           {
  869.         VisibleTreeNode lastChild = (VisibleTreeNode)parentNode.getLastChild();
  870.         drawVerticalPartOfLeg( g, c, parentNode.getVisibleLevel(), getNodeY( parentNode ),
  871.                        getNodeY( lastChild ), getNodeHeight( parentNode ),
  872.                        getNodeHeight( lastChild ) );
  873.  
  874.         drawingCache.addElement( parentNode );
  875.         parentNode = (VisibleTreeNode)parentNode.getParent();
  876.           }
  877.         
  878.         for ( int row = beginRow; row <= endRow; ++row )
  879.           {
  880.             childNode = getNode( row );
  881.         parentNode = (VisibleTreeNode)childNode.getParent();
  882.  
  883.         if ( parentNode != null && !drawingCache.contains( parentNode )  )
  884.           {
  885.             VisibleTreeNode lastChild = (VisibleTreeNode)parentNode.getLastChild();
  886.             drawVerticalPartOfLeg( g, c, parentNode.getVisibleLevel(), getNodeY( parentNode ),
  887.                        getNodeY( lastChild ), getNodeHeight( parentNode ),
  888.                        getNodeHeight( lastChild ) );
  889.  
  890.             drawingCache.addElement( parentNode );
  891.           }
  892.  
  893.         if ( parentNode != null )
  894.           {
  895.             drawHorizontalPartOfLeg( g, c,
  896.                          getNodeY( childNode ) + getNodeHeight( childNode ) / 2,
  897.                          getNodeX( parentNode ) + 8,
  898.                          getNodeX( childNode ) );
  899.           }
  900.         
  901.         int childX = getNodeX( childNode );
  902.         int childY = getNodeY( childNode );
  903.         int childRowHeight = getNodeHeight( childNode );
  904.  
  905.         // Paints the 'knobs' like the + and - squares for collapsing and expanding.
  906.         if ( shouldPaintExpandControl( parentNode, childNode ) )
  907.           {
  908.             paintExpandControl( g, c, parentNode, childNode,
  909.                     childX, childY,
  910.                     childRowHeight, row );
  911.           }
  912.  
  913.         // Paints the main contents of the row
  914.             paintRow( g, c, parentNode, childNode,
  915.               childX, childY,
  916.               childRowHeight, row );
  917.           }
  918.  
  919.         drawingCache.removeAllElements();
  920.     }
  921.     }
  922.  
  923.     protected boolean shouldPaintExpandControl( VisibleTreeNode parentNode, VisibleTreeNode childNode )
  924.       {
  925.     boolean result = true;
  926.  
  927.     if ( (childNode.getLevel() == 0 && !getShowsRootHandles()) ||
  928.          (childNode.getLevel() == 1 && !isRootVisible() && !getShowsRootHandles()) )
  929.       {
  930.         result = false;
  931.       }
  932.  
  933.     return result;
  934.       }
  935.  
  936.     protected int getNodeX( VisibleTreeNode node )
  937.       {
  938.     int levelOffset = getShowsRootHandles() ? 1 : 0;
  939.     return (node.getVisibleLevel() + levelOffset) * totalChildIndent;
  940.       }
  941.  
  942.     protected int getNodeY( VisibleTreeNode node )
  943.       {
  944.     return node.getYOrigin();
  945.       }
  946.  
  947.     protected int getNodeHeight( VisibleTreeNode node )
  948.       {
  949.     return isFixedRowHeight() ? getRowHeight() : node.getPreferredSize().height;
  950.       }
  951. /*
  952.     protected void drawVerticalPartOfLeg( Graphics g, JComponent c,
  953.                       VisibleTreeNode parentNode, VisibleTreeNode childNode )
  954.       {
  955.     int parentX = getNodeX( parentNode );
  956.     int lineX = parentX + 8;
  957.  
  958.     Rectangle clipBounds = g.getClipBounds();
  959.     int clipLeft = clipBounds.x;
  960.     int clipRight = clipBounds.x + (clipBounds.width - 1);
  961.  
  962.     if ( lineX > clipLeft && lineX < clipRight )
  963.       {
  964.         int clipTop = clipBounds.y;
  965.         int clipBottom = clipBounds.y + (clipBounds.height - 1);
  966.  
  967.         int parentY = getNodeY( parentNode );
  968.         int parentRowHeight = getNodeHeight( parentNode );
  969.  
  970.         int childY = getNodeY( childNode );
  971.         int childRowHeight = getNodeHeight( childNode );
  972.  
  973.         int top = Math.max( parentY + (parentRowHeight - 1) + getVerticalLegBuffer(), clipTop );
  974.         int bottom = Math.min( childY + (childRowHeight / 2), clipBottom );
  975.     
  976.         g.setColor( getHashColor() );
  977.         drawVerticalLine( g, c, lineX, top, bottom );
  978.       }
  979.       }
  980. */
  981.     public void drawVerticalPartOfLeg( Graphics g, JComponent c, int depth, int parentY, int childY,
  982.                        int parentRowHeight, int childRowHeight ) {
  983.     int levelOffset = getShowsRootHandles() ? 1 : 0;
  984.         int lineX = ((depth + levelOffset) * totalChildIndent) + 8;
  985.  
  986.     Rectangle clipBounds = g.getClipBounds();
  987.     int clipLeft = clipBounds.x;
  988.     int clipRight = clipBounds.x + (clipBounds.width - 1);
  989.  
  990.     if ( lineX > clipLeft && lineX < clipRight )
  991.       {
  992.         int clipTop = clipBounds.y;
  993.         int clipBottom = clipBounds.y + clipBounds.height;
  994.  
  995.         int top = Math.max( parentY + parentRowHeight + getVerticalLegBuffer(), clipTop );
  996.         int bottom = Math.min( childY + (childRowHeight / 2), clipBottom );
  997.     
  998.         g.setColor( getHashColor() );
  999.         drawVerticalLine( g, c, lineX, top, bottom );
  1000.       }
  1001.     }
  1002.  
  1003.     /**
  1004.      * The vertical element of legs between nodes starts at the bottom of the
  1005.      * parent node by default.  This method makes the leg start below that.
  1006.      */
  1007.     protected int getVerticalLegBuffer()
  1008.       {
  1009.     return 0;
  1010.       } 
  1011.  
  1012.     /**
  1013.      * The horizontal element of legs between nodes starts at the right of the
  1014.      * left-hand side of the child node by default.  This method makes the leg end before that.
  1015.      */
  1016.     protected int getHorizontalLegBuffer()
  1017.       {
  1018.     return 0;
  1019.       } 
  1020.  
  1021.     protected void drawVerticalLine( Graphics g, JComponent c, int x, int top, int bottom )
  1022.       {
  1023.     g.drawLine( x, top, x, bottom );
  1024.       }
  1025. /*
  1026.     protected void drawHorizontalPartOfLeg( Graphics g, JComponent c,
  1027.                         VisibleTreeNode parentNode, VisibleTreeNode childNode )
  1028.       {
  1029.     Rectangle clipBounds = g.getClipBounds();
  1030.     int clipLeft = clipBounds.x;
  1031.     int clipRight = clipBounds.x + (clipBounds.width - 1);
  1032.     int clipTop = clipBounds.y;
  1033.     int clipBottom = clipBounds.y + (clipBounds.height - 1);
  1034.  
  1035.     int childY = getNodeY( childNode );
  1036.     int childRowHeight = getNodeHeight( childNode );
  1037.     int lineY = childY + (childRowHeight / 2);
  1038.  
  1039.     int parentX = getNodeX( parentNode );
  1040.     int childX = getNodeX( childNode );
  1041.  
  1042.     int left = parentX + 8;
  1043.     int right = childX - getHorizontalLegBuffer();
  1044.  
  1045.     if ( lineY > clipTop && lineY < clipBottom && right > clipLeft && left < clipRight )
  1046.       {
  1047.         left = Math.max( left, clipLeft );
  1048.         right = Math.min( right, clipRight );
  1049.  
  1050.         g.setColor( getHashColor() );
  1051.         drawHorizontalLine(g, c, lineY, left, right );
  1052.       }
  1053.       }
  1054. */
  1055.  
  1056.     public void drawHorizontalPartOfLeg( Graphics g, JComponent c, int lineY, int leftX, int rightX  )
  1057.       {
  1058.     Rectangle clipBounds = g.getClipBounds();
  1059.     int clipLeft = clipBounds.x;
  1060.     int clipRight = clipBounds.x + (clipBounds.width - 1);
  1061.     int clipTop = clipBounds.y;
  1062.     int clipBottom = clipBounds.y + (clipBounds.height - 1);
  1063.  
  1064.     rightX -= getHorizontalLegBuffer();
  1065.     
  1066.     if ( lineY > clipTop && lineY < clipBottom && rightX > clipLeft && leftX < clipRight )
  1067.       {
  1068.         leftX = Math.max( leftX, clipLeft );
  1069.         rightX = Math.min( rightX, clipRight );
  1070.  
  1071.         g.setColor( getHashColor() );
  1072.         drawHorizontalLine(g, c, lineY, leftX, rightX );
  1073.       }
  1074.       }
  1075.  
  1076.     protected void drawHorizontalLine( Graphics g, JComponent c, int y, int left, int right )
  1077.       {
  1078.     g.drawLine( left, y, right, y );
  1079.       }
  1080.  
  1081.     protected void paintRow( Graphics g, JComponent c,
  1082.                  VisibleTreeNode parentNode, VisibleTreeNode childNode,
  1083.                  int childX, int childY,
  1084.                  int childRowHeight, int row )
  1085.       {
  1086.     int leadIndex = -1;
  1087.  
  1088.     if(tree.hasFocus())
  1089.       {
  1090.         leadIndex = tree.getLeadSelectionRow();
  1091.       }
  1092.  
  1093.     Component component = getCellRenderer().getTreeCellRendererComponent(tree,
  1094.                                childNode.getValue(), isSelectedIndex(row),
  1095.                                childNode.isExpanded(),childNode.isLeaf(), row,
  1096.                                (leadIndex == row));
  1097.     
  1098.     rendererPane.paintComponent(g, component,tree, childX, childY, 
  1099.                     childNode.getPreferredSize().width,
  1100.                     childRowHeight, true);    
  1101.       }
  1102.  
  1103.     protected void paintExpandControl( Graphics g, JComponent c,
  1104.                        VisibleTreeNode parentNode, VisibleTreeNode childNode,
  1105.                        int childX, int childY,
  1106.                        int childRowHeight, int row )
  1107.       {
  1108.     // Draw icons if not a leaf and either hasn't been loaded,
  1109.     // or the model child count is > 0.
  1110.     if (!childNode.isLeaf() && (!childNode.hasBeenExpanded() ||
  1111.                    childNode.getModelChildCount() > 0))
  1112.       {
  1113.         int middleXOfKnob = childX - (getRightChildIndent() - 1);
  1114.         int middleYOfKnob = childY + (childRowHeight / 2);
  1115.  
  1116.         if (childNode.isExpanded())
  1117.           {
  1118.         Icon expandedIcon = getExpandedIcon();
  1119.         if(expandedIcon != null)
  1120.           drawCentered( c, g, expandedIcon, middleXOfKnob, middleYOfKnob );
  1121.           }
  1122.         else
  1123.           {
  1124.         Icon collapsedIcon = getCollapsedIcon();
  1125.         if(collapsedIcon != null)
  1126.           drawCentered( c, g, collapsedIcon, middleXOfKnob, middleYOfKnob );
  1127.           }
  1128.       }
  1129.       }
  1130.  
  1131.     // Draws the icon centered at (x,y)
  1132.     protected void drawCentered(Component c, Graphics graphics, Icon icon, int x, int y) {
  1133.     icon.paintIcon(c, graphics, x - icon.getIconWidth()/2, y - icon.getIconHeight()/2);
  1134.     }
  1135.  
  1136.     // This method is slow -- revisit when Java2D is ready.
  1137.     // assumes x1 <= x2
  1138.     protected void drawDashedHorizontalLine(Graphics g, int y, int x1, int x2){
  1139.     // Drawing only even coordinates helps join line segments so they
  1140.     // appear as one line.  This can be defeated by translating the
  1141.     // Graphics by an odd amount.
  1142.     x1 += (x1 % 2);
  1143.  
  1144.     for (int x = x1; x <= x2; x+=2) {
  1145.         g.drawLine(x, y, x, y);
  1146.     }
  1147.     }
  1148.  
  1149.     // This method is slow -- revisit when Java2D is ready.
  1150.     // assumes y1 <= y2
  1151.     protected void drawDashedVerticalLine(Graphics g, int x, int y1, int y2) {
  1152.     // Drawing only even coordinates helps join line segments so they
  1153.     // appear as one line.  This can be defeated by translating the
  1154.     // Graphics by an odd amount.
  1155.     y1 += (y1 % 2);
  1156.  
  1157.     for (int y = y1; y <= y2; y+=2) {
  1158.         g.drawLine(x, y, x, y);
  1159.     }
  1160.     }
  1161.  
  1162.     /** Sets the preferred minimum size.
  1163.       */
  1164.     public void setPreferredMinSize(Dimension newSize)
  1165.     {
  1166.     preferredMinSize = newSize;
  1167.     }
  1168.  
  1169.     /** Returns the minimum preferred size.
  1170.       */
  1171.     public Dimension getPreferredMinSize()
  1172.     {
  1173.     return preferredMinSize;
  1174.     }
  1175.  
  1176.     /** Returns the preferred size to properly display the tree,
  1177.       * this is a cover method for getPreferredSize(c, false).
  1178.       */
  1179.     public Dimension getPreferredSize(JComponent c) {
  1180.     return getPreferredSize(c, true);
  1181.     }
  1182.  
  1183.     /** Returns the preferred size to represent the tree in
  1184.       * <I>c</I>.  If <I>checkConsistancy</I> is true
  1185.       * <b>checkConsistancy</b> is messaged first.
  1186.       */
  1187.     public Dimension getPreferredSize(JComponent c,
  1188.                       boolean checkConsistancy) {
  1189.     Dimension       pSize = this.getPreferredMinSize();
  1190.  
  1191.     if(checkConsistancy)
  1192.         this.checkConsistency();
  1193.     if(!validCachedPreferredSize)
  1194.         updateCachedPreferredSize();
  1195.     if(tree != null)
  1196.     {
  1197.         if(pSize != null)
  1198.         return new Dimension(Math.max(pSize.width,
  1199.                           cPreferredSize.width),
  1200.                   Math.max(pSize.height, cPreferredSize.height));
  1201.         return new Dimension(cPreferredSize.width, cPreferredSize.height);
  1202.     }
  1203.     else if(pSize != null)
  1204.         return pSize;
  1205.     else
  1206.         return new Dimension(0, 0);
  1207.     }
  1208.  
  1209.     /**
  1210.       * Returns the minimum size for this component.  Which will be
  1211.       * the min preferred size or 0, 0.
  1212.       */
  1213.     public Dimension getMinimumSize(JComponent c) {
  1214.     if(this.getPreferredMinSize() != null)
  1215.         return this.getPreferredMinSize();
  1216.     return new Dimension(0, 0);
  1217.     }
  1218.  
  1219.     /**
  1220.       * Returns the maximum size for this component, which will be the
  1221.       * preferred size if the instance is currently in a JTree, or 0, 0.
  1222.       */
  1223.     public Dimension getMaximumSize(JComponent c) {
  1224.     if(tree != null)
  1225.         return getPreferredSize(tree);
  1226.     if(this.getPreferredMinSize() != null)
  1227.         return this.getPreferredMinSize();
  1228.     return new Dimension(0, 0);
  1229.     }
  1230.  
  1231.  
  1232.     /**
  1233.       *  Messaged when the selection changes in the tree we're displaying
  1234.       * for.  Stops editing, messages super and displays the changed paths.
  1235.       */
  1236.     public void valueChanged(TreeSelectionEvent event) {
  1237.     completeEditing();
  1238.     super.valueChanged(event);
  1239.     lastSelectedRow = getMinSelectionRow();
  1240.  
  1241.     TreePath[]       changedPaths = event.getPaths();
  1242.     Rectangle        nodeBounds;
  1243.     Rectangle        visRect = tree.getVisibleRect();
  1244.     boolean          paintPaths = true;
  1245.     int              nWidth = tree.getSize().width;
  1246.  
  1247.     if(changedPaths != null) {
  1248.         int              counter, maxCounter = changedPaths.length;
  1249.  
  1250.         if(maxCounter > 4) {
  1251.         tree.repaint();
  1252.         paintPaths = false;
  1253.         }
  1254.         else {
  1255.         VisibleTreeNode          aNode;
  1256.  
  1257.         for (counter = 0; counter < maxCounter; counter++) {
  1258.             nodeBounds = getPathBounds(changedPaths[counter]);
  1259.             if(nodeBounds != null && visRect.intersects(nodeBounds))
  1260.             tree.repaint(0, nodeBounds.y, nWidth,
  1261.                      nodeBounds.height);
  1262.         }
  1263.         }
  1264.     }
  1265.     if(paintPaths) {
  1266.         nodeBounds = getPathBounds(event.getOldLeadSelectionPath());
  1267.         if(nodeBounds != null && visRect.intersects(nodeBounds))
  1268.         tree.repaint(0, nodeBounds.y, nWidth, nodeBounds.height);
  1269.         nodeBounds = getPathBounds(event.getNewLeadSelectionPath());
  1270.         if(nodeBounds != null && visRect.intersects(nodeBounds))
  1271.         tree.repaint(0, nodeBounds.y, nWidth, nodeBounds.height);
  1272.     }
  1273.     }
  1274.  
  1275.     /**
  1276.       * Stops the editing session by messaging stopCellEditing(false).
  1277.       */
  1278.     public void editingStopped(ChangeEvent e) {
  1279.     completeEditing(false, false, true);
  1280.     }
  1281.  
  1282.     /**
  1283.       * Stops the editing session by messaging cancelEditing(false).
  1284.       */
  1285.     public void editingCanceled(ChangeEvent e) {
  1286.     completeEditing(false, false, false);
  1287.     }
  1288.  
  1289.     /**
  1290.       * Returns true if the tree is being edited.  The item that is being
  1291.       * edited can be returned by getSelectionPath().
  1292.       */
  1293.     public boolean isEditing() {
  1294.     return (editingComponent != null);
  1295.     }
  1296.  
  1297.     /**
  1298.       * Stops the current editing session, returns true if the tree is
  1299.       * current editing and the editor returns true from stopCellEditing().
  1300.       */
  1301.     public boolean stopEditing() {
  1302.     if(editingComponent != null && cellEditor.stopCellEditing()) {
  1303.         completeEditing(false, false, true);
  1304.         return true;
  1305.     }
  1306.     return false;
  1307.     }
  1308.  
  1309.     /**
  1310.       * Cancels the current editing session.
  1311.       */
  1312.     public void cancelEditing() {
  1313.     if(editingComponent != null) {
  1314.         completeEditing(false, true, false);
  1315.     }
  1316.     }
  1317.  
  1318.     /**
  1319.      * Messages to stop the editing session. If the UI the receiver
  1320.      * is providing the look and feel for returns true from
  1321.      * <code>getInvokesStopCellEditing</code>, stopCellEditing will
  1322.      * invoked on the current editor. Then completeEditing will
  1323.      * be messaged with false, true, false to cancel any lingering
  1324.      * editing.
  1325.      */
  1326.     protected void completeEditing() {
  1327.     /* If should invoke stopCellEditing, try that */
  1328.     if(tree.getInvokesStopCellEditing() &&
  1329.        stopEditingInCompleteEditing && editingComponent != null) {
  1330.         cellEditor.stopCellEditing();
  1331.     }
  1332.     /* Invoke cancelCellEditing, this will do nothing if stopCellEditing
  1333.        was succesful. */
  1334.     completeEditing(false, true, false);
  1335.     }
  1336.  
  1337.     /**
  1338.       * Stops the editing session.  If messageStop is true the editor
  1339.       * is messaged with stopEditing, if messageCancel is true the
  1340.       * editor is messaged with cancelEditing. If messageTree is true
  1341.       * the treeModel is messaged with valueForPathChanged.
  1342.       */
  1343.     protected void completeEditing(boolean messageStop,
  1344.                    boolean messageCancel,
  1345.                    boolean messageTree) {
  1346.     if(stopEditingInCompleteEditing && editingComponent != null) {
  1347.         Component             oldComponent = editingComponent;
  1348.         TreePath              oldPath = editingPath;
  1349.         TreeCellEditor        oldEditor = cellEditor;
  1350.         Object                newValue = oldEditor.getCellEditorValue();
  1351.         Rectangle             editingBounds = getPathBounds(editingPath);
  1352.  
  1353.         editingComponent = null;
  1354.         editingPath = null;
  1355.         if(messageStop)
  1356.         oldEditor.stopCellEditing();
  1357.         else if(messageCancel)
  1358.         oldEditor.cancelCellEditing();
  1359.         tree.remove(oldComponent);
  1360.         editingBounds.x = 0;
  1361.         editingBounds.width = tree.getSize().width;
  1362.         tree.repaint(editingBounds);
  1363.         if(messageTree)
  1364.         treeModel.valueForPathChanged(oldPath, newValue);
  1365.     }
  1366.     }
  1367.  
  1368.     /**
  1369.       * Selects the last item in path and tries to edit it.  Editing will
  1370.       * fail if the CellEditor won't allow it for the selected item.
  1371.       */
  1372.     public void startEditingAtPath(TreePath path) {
  1373.     VisibleTreeNode   aNode;
  1374.  
  1375.     scrollPathToVisible(path);
  1376.     if(path != null && isVisible(path))
  1377.         startEditing(path, null);
  1378.     }
  1379.  
  1380.     /**
  1381.       * Will start editing for node if there is a cellEditor and
  1382.       * shouldSelectCell returns true.<p>
  1383.       * This assumes that path is valid and visible.
  1384.       */
  1385.     protected boolean startEditing(TreePath path, MouseEvent event) {
  1386.     completeEditing();
  1387.     if(cellEditor != null && tree.isPathEditable(path)) {
  1388.         int           row = getRowForPath(path);
  1389.  
  1390.         editingComponent = cellEditor.getTreeCellEditorComponent
  1391.               (tree, path.getLastPathComponent(), isPathSelected(path),
  1392.                isExpanded(path), treeModel.isLeaf
  1393.                (path.getLastPathComponent()), row);
  1394.         if(cellEditor.isCellEditable(event)) {
  1395.         Rectangle           nodeBounds = getPathBounds(path);
  1396.  
  1397.         if(editingComponent.getFont() == null) {
  1398.             editingComponent.setFont(tree.getFont());
  1399.         }
  1400.         Dimension editorSize = editingComponent.getPreferredSize();
  1401.  
  1402.         tree.add(editingComponent);
  1403.         editingComponent.setBounds(nodeBounds.x, nodeBounds.y,
  1404.                        nodeBounds.width,
  1405.                        nodeBounds.height);
  1406.         editingPath = path;
  1407.         editingComponent.validate();
  1408.         tree.paintImmediately(nodeBounds.x, nodeBounds.y,
  1409.                  nodeBounds.width, nodeBounds.height);    
  1410.         if(cellEditor.shouldSelectCell(event)) {
  1411.             stopEditingInCompleteEditing = false;
  1412.             try {
  1413.             tree.setSelectionRow(row);
  1414.             } catch (Exception e) {
  1415.             System.out.println("Editing exception: " + e);
  1416.             }
  1417.             stopEditingInCompleteEditing = true;
  1418.         }
  1419.  
  1420.         if(event != null && event instanceof MouseEvent) {
  1421.             /* Find the component that will get forwarded all the
  1422.                mouse events until mouseReleased. */
  1423.             Point          componentPoint = SwingUtilities.convertPoint
  1424.             (tree, new Point(event.getX(), event.getY()),
  1425.              editingComponent);
  1426.  
  1427.             /* Create an instance of BasicTreeMouseListener to handle
  1428.                passing the mouse/motion events to the necessary
  1429.                component. */
  1430.             new BasicTreeMouseListener(tree, SwingUtilities
  1431.                  .getDeepestComponentAt(editingComponent,
  1432.                     componentPoint.x, componentPoint.y),
  1433.                            event);
  1434.         }
  1435.         return true;
  1436.         }
  1437.         else
  1438.         editingComponent = null;
  1439.     }
  1440.     return false;
  1441.     }
  1442.  
  1443.     /**
  1444.      * Returns the path to the element that is being edited.
  1445.      */
  1446.     public TreePath getEditingPath() {
  1447.     return editingPath;
  1448.     }
  1449.  
  1450.     /**
  1451.      * Invoked when the mouse has been clicked on a component.
  1452.      */
  1453.     public void mouseClicked(MouseEvent e) {
  1454.     }
  1455.  
  1456.     /**
  1457.      * Invoked when a mouse button has been pressed on a component.
  1458.      */
  1459.     public void mousePressed(MouseEvent e) {
  1460.     int row;
  1461.  
  1462.     if(tree != null && tree.isEnabled())
  1463.         tree.requestFocus();
  1464.     checkConsistency();
  1465.     if(tree != null && tree.isEnabled()) {
  1466.         row = this.getRowContainingYLocation(e.getY());
  1467.         /* getRowContainingYLocation will return the last row,
  1468.            even if outside of it, this insures it isn't beyond the
  1469.            last row. */
  1470.         if(row != -1) {
  1471.         Rectangle         rect = getRowBounds(row);
  1472.  
  1473.         if(e.getY() > (rect.y + rect.height)) {
  1474.             row = -1;
  1475.         }
  1476.         }
  1477.     }
  1478.     else
  1479.         row = -1;
  1480.  
  1481.     if (/*SwingUtils.isLeftMouseButton(e) && */ row > -1 &&
  1482.         row < this.getRowCount()) {
  1483.         int                      rowLevel;
  1484.         int                      childIndex = -1;
  1485.         LargeTreeModelNode       eNode = null;
  1486.         VisibleTreeNode          node = null;
  1487.  
  1488.         if(!largeModel) {
  1489.         node = this.getNode(row);
  1490.         rowLevel = node.getVisibleLevel();
  1491.         }
  1492.         else {
  1493.         if(row == 0 && isRootVisible()) {
  1494.             eNode = largeRoot;
  1495.             rowLevel = 0;
  1496.         }
  1497.         else {
  1498.             int[]           cIndex = new int[1];
  1499.  
  1500.             eNode = getLargeParentAndChildIndexOfRow(row, cIndex);
  1501.             rowLevel = eNode.getVisibleLevel() + 1;
  1502.             childIndex = cIndex[0];
  1503.         }
  1504.         }
  1505.  
  1506.         checkForClickInExpandControl( node, eNode, childIndex, row, rowLevel, e.getX(), e.getY() );
  1507.         
  1508.         int x = e.getX();
  1509.  
  1510.         // Perhaps they clicked the cell itself. If so,
  1511.         // select it.
  1512.         int     cellLeftX;
  1513.         if(this.getShowsRootHandles())
  1514.         cellLeftX = totalChildIndent * (rowLevel + 1);
  1515.         else
  1516.         cellLeftX = totalChildIndent * rowLevel;
  1517.  
  1518.         if (x > cellLeftX) {
  1519.         int cellWidth;
  1520.  
  1521.         if(!largeModel)
  1522.             cellWidth = node.getPreferredSize().width;
  1523.         else
  1524.             cellWidth = getRowBounds(row).width;
  1525.         if (x <= cellWidth + cellLeftX && 
  1526.             !startEditing(getPathForRow(row), e)) {
  1527.             int        originalSelectedIndex;
  1528.  
  1529.             originalSelectedIndex = lastSelectedRow;
  1530.             if(originalSelectedIndex >= this.getRowCount() ||
  1531.                !this.isSelectedIndex(originalSelectedIndex))
  1532.             originalSelectedIndex = -1;
  1533.  
  1534.             /* Control toggles just this node. */
  1535.             if(e.isControlDown()) {
  1536.             if(isSelectedIndex(row))
  1537.                 tree.removeSelectionInterval(row, row);
  1538.             else
  1539.                 tree.addSelectionInterval(row, row);
  1540.             if(originalSelectedIndex != -1)
  1541.                 lastSelectedRow = originalSelectedIndex;
  1542.             }
  1543.             /* Shift adjusts from the anchor point. */
  1544.             else if(e.isShiftDown())
  1545.             {
  1546.             if(originalSelectedIndex == -1)
  1547.                 tree.addSelectionInterval(row, row);
  1548.             else
  1549.             {
  1550.                 if(row < originalSelectedIndex)
  1551.                 tree.setSelectionInterval(row,
  1552.                      originalSelectedIndex);
  1553.                 else
  1554.                 tree.setSelectionInterval
  1555.                     (originalSelectedIndex, row);
  1556.                 lastSelectedRow = originalSelectedIndex;
  1557.             }
  1558.             if(originalSelectedIndex != -1)
  1559.                 lastSelectedRow = originalSelectedIndex;
  1560.             }
  1561.             /* Otherwise set the selection to just this interval. */
  1562.             else {
  1563.             tree.setSelectionInterval(row, row);
  1564.             if(e.getClickCount() == 2) {
  1565.                 if(!largeModel) {
  1566.                 node.toggleExpanded();
  1567.                 ensureRowsAreVisible(row, row + Math.min
  1568.                      (10, node.visibleChildCount()));
  1569.                 }
  1570.                 else {
  1571.                 if(childIndex == -1) {
  1572.                     eNode.toggleExpanded();
  1573.                     ensureRowsAreVisible(row, row + Math.min
  1574.                      (10, eNode.getTotalChildCount()));
  1575.                 }
  1576.                 else {
  1577.                     if(!isExpanded(row))
  1578.                     expandRow(row);
  1579.                     else
  1580.                     collapseRow(row);
  1581.  
  1582.                     LargeTreeModelNode     newNode;
  1583.  
  1584.                     newNode = getLargeTreeModelNodeForRow
  1585.                         (row, false);
  1586.                     if(newNode != null)
  1587.                     ensureRowsAreVisible
  1588.                        (row, row + Math.min
  1589.                        (10, newNode.getTotalChildCount()));
  1590.                     else
  1591.                     ensureRowsAreVisible(row, row);
  1592.                 }
  1593.                 }
  1594.             }
  1595.             }
  1596.         }
  1597.         }
  1598.  
  1599.         // PENDING: Should select on mouse down, start a drag if
  1600.         // the mouse moves, and fire selection change notice on
  1601.         // mouse up. That is, the explorer highlights on mouse
  1602.         // down, but doesn't update the pane to the right (and
  1603.         // open the folder icon) until mouse up.
  1604.     }
  1605.     }
  1606.  
  1607.     protected void checkForClickInExpandControl( VisibleTreeNode node, LargeTreeModelNode eNode,
  1608.                          int childIndex, int row,
  1609.                          int rowLevel, int mouseX, int mouseY ) {
  1610.       if ( clickedInExpandControl( node, eNode, row, rowLevel, mouseX, mouseY ) )
  1611.     {
  1612.       handleExpandControlClick( node, eNode, childIndex, row );
  1613.     }
  1614.     }
  1615.  
  1616.     protected boolean clickedInExpandControl( VisibleTreeNode node, LargeTreeModelNode eNode,
  1617.                           int row, int rowLevel, int mouseX, int mouseY )
  1618.       {
  1619.     if ( node != null && node.isLeaf() )
  1620.       {
  1621.         return false;
  1622.       }
  1623.  
  1624.     int                     boxWidth;
  1625.  
  1626.     if(getExpandedIcon() != null)
  1627.         boxWidth = getExpandedIcon().getIconWidth();
  1628.     else
  1629.         boxWidth = 8;
  1630.     int    boxLeftX;
  1631.     if(this.getShowsRootHandles())
  1632.         boxLeftX = ((rowLevel * totalChildIndent) +
  1633.             getLeftChildIndent()) - boxWidth/2;
  1634.     else
  1635.         boxLeftX = (((rowLevel - 1) * totalChildIndent) +
  1636.             getLeftChildIndent()) - boxWidth/2;
  1637.     int boxRightX = boxLeftX + boxWidth;
  1638.     
  1639.     return mouseX >= boxLeftX && mouseX <= boxRightX;
  1640.       }
  1641.  
  1642.     public void handleExpandControlClick( VisibleTreeNode node, LargeTreeModelNode eNode,
  1643.                       int childIndex, int row ) {
  1644.         if(!largeModel) {
  1645.           node.toggleExpanded();
  1646.         ensureRowsAreVisible(row, row + Math.min
  1647.                  (10, node.visibleChildCount()));
  1648.     }
  1649.     else {
  1650.         if(childIndex == -1) {
  1651.             eNode.toggleExpanded();
  1652.         ensureRowsAreVisible(row, row + Math.min
  1653.                      (10, eNode.getTotalChildCount()));
  1654.         }
  1655.         else {
  1656.             toggleExpandState( row );
  1657.  
  1658.         LargeTreeModelNode     newNode;
  1659.  
  1660.         newNode = getLargeTreeModelNodeForRow(row, false);
  1661.         if ( newNode != null ) {
  1662.             ensureRowsAreVisible(row, row + Math.min
  1663.                      (10, newNode.getTotalChildCount()));
  1664.         }
  1665.         }
  1666.     }
  1667.     }
  1668.  
  1669.     protected void toggleExpandState( int row )
  1670.       {
  1671.     if ( !isExpanded( row ) )
  1672.       {
  1673.         expandRow( row );
  1674.       }
  1675.     else
  1676.         {
  1677.         collapseRow( row );
  1678.       }
  1679.       }
  1680.  
  1681.     /**
  1682.      * Invoked when a mouse button has been released on a component.
  1683.      */
  1684.     public void mouseReleased(MouseEvent e) {
  1685.     }
  1686.  
  1687.     /**
  1688.      * Invoked when the mouse enters a component.
  1689.      */
  1690.     public void mouseEntered(MouseEvent e) {
  1691.     }
  1692.  
  1693.     /**
  1694.      * Invoked when the mouse exits a component.
  1695.      */
  1696.     public void mouseExited(MouseEvent e) {
  1697.     }
  1698.  
  1699.     /**
  1700.       * Invoked when focus is activated on the tree we're in, redraws the
  1701.       * lead row.
  1702.       */
  1703.     public void focusGained(FocusEvent e) {
  1704.     if(tree != null) {
  1705.         int                  leadRow = tree.getLeadSelectionRow();
  1706.  
  1707.         if(leadRow != -1)
  1708.         tree.repaint(getRowBounds(leadRow));
  1709.     }
  1710.     }
  1711.  
  1712.     /**
  1713.       * Invoked when focus is activated on the tree we're in, redraws the
  1714.       * lead row.
  1715.       */
  1716.     public void focusLost(FocusEvent e) {
  1717.     focusGained(e);
  1718.     }
  1719.  
  1720.     public void keyPressed(KeyEvent e) {
  1721.     if(tree.hasFocus() && tree.isEnabled()) {
  1722.         KeyStroke       keyStroke = KeyStroke.getKeyStroke
  1723.            (e.getKeyCode(), e.getModifiers());
  1724.  
  1725.         if(tree.getConditionForKeyStroke(keyStroke) ==
  1726.            JComponent.WHEN_FOCUSED) {
  1727.         ActionListener     listener = tree.
  1728.                            getActionForKeyStroke(keyStroke);
  1729.  
  1730.         if(listener instanceof Action)
  1731.             repeatKeyAction = (Action)listener;
  1732.         else
  1733.             repeatKeyAction = null;
  1734.         }
  1735.         else
  1736.         repeatKeyAction = null;
  1737.         if(isKeyDown && repeatKeyAction != null) {
  1738.         repeatKeyAction.actionPerformed(null);
  1739.         e.consume();
  1740.         }
  1741.         else
  1742.         isKeyDown = true;
  1743.     }
  1744.     }
  1745.  
  1746.     public void keyReleased(KeyEvent e) {
  1747.     isKeyDown = false;
  1748.     }
  1749.  
  1750.     public void keyTyped(KeyEvent e) {
  1751.     }
  1752.  
  1753.     // Following are subclassed to stopEditing before passing to super.
  1754.  
  1755.     /**
  1756.       * Stops editing and messages super.
  1757.       */
  1758.     public void setModel(TreeModel newModel) {
  1759.     completeEditing();
  1760.     super.setModel(newModel);
  1761.     }
  1762.  
  1763.     /**
  1764.       * Stops editing and messags super.
  1765.       */
  1766.     public void rebuild() {
  1767.     completeEditing();
  1768.     super.rebuild();
  1769.     }
  1770.  
  1771.     /**
  1772.       * Stops editing, messags super and becomes a listener on the model.
  1773.       */
  1774.     public void setSelectionModel(TreeSelectionModel newLSM) {
  1775.     completeEditing();
  1776.     if(treeSelectionModel != null)
  1777.         treeSelectionModel.removePropertyChangeListener(this);
  1778.     super.setSelectionModel(newLSM);
  1779.     if(treeSelectionModel != null)
  1780.         treeSelectionModel.addPropertyChangeListener(this);
  1781.     if(tree != null)
  1782.         tree.repaint();
  1783.     }
  1784.  
  1785.     /**
  1786.       * Creates an instance of BasicVisibleTreeNode.
  1787.       */
  1788.     protected VisibleTreeNode createNodeForValue(Object value, int index) {
  1789.     return new BasicVisibleTreeNode(this, value, index);
  1790.     }
  1791.  
  1792.     /**
  1793.       * Creates an instance of BasicLargeTreeModelNode.
  1794.       */
  1795.     protected LargeTreeModelNode createLargeTreeModelNodeForValue(Object value,
  1796.                               int childIndex) {
  1797.     return new BasicLargeTreeModelNode(this, value, childIndex);
  1798.     }
  1799.  
  1800.     /**
  1801.       * Repaints the particular node by getting its bounds.
  1802.       */
  1803.     protected void repaintNode(BasicLargeTreeModelNode node) {
  1804.     if(tree != null)
  1805.         tree.repaint(0, node.getRow() * rowHeight, tree.getSize().width,
  1806.              rowHeight);
  1807.     }
  1808.  
  1809.     /**
  1810.       * Repaints the particular node by getting its bounds.
  1811.       */
  1812.     protected void repaintNode(VisibleTreeNode node) {
  1813.     Rectangle        nodeBounds = node.getNodeBounds();
  1814.  
  1815.     if(tree != null)
  1816.         tree.repaint(0, nodeBounds.y, tree.getSize().width,
  1817.              nodeBounds.height);
  1818.     }
  1819.  
  1820.  
  1821.     // Serialization support.  
  1822.     private void writeObject(ObjectOutputStream s) throws IOException {
  1823.     Vector      values = new Vector();
  1824.  
  1825.     s.defaultWriteObject();
  1826.     // Save the collapsedIcon, if its Serializable.
  1827.     if(collapsedIcon != null && collapsedIcon instanceof Serializable) {
  1828.         values.addElement("collapsedIcon");
  1829.         values.addElement(collapsedIcon);
  1830.     }
  1831.     // Save the expandedIcon, if its Serializable.
  1832.     if(expandedIcon != null && expandedIcon instanceof Serializable) {
  1833.         values.addElement("expandedIcon");
  1834.         values.addElement(expandedIcon);
  1835.     }
  1836.     // Save the currentCellRenderer, if its Serializable.
  1837.     if(currentCellRenderer != null &&
  1838.        currentCellRenderer instanceof Serializable) {
  1839.         values.addElement("currentCellRenderer");
  1840.         values.addElement(currentCellRenderer);
  1841.     }
  1842.     // Save the cellEditor, if its Serializable.
  1843.     if(cellEditor != null && cellEditor instanceof Serializable) {
  1844.         values.addElement("cellEditor");
  1845.         values.addElement(cellEditor);
  1846.     }
  1847.     s.writeObject(values);
  1848.     }
  1849.  
  1850.     private void readObject(ObjectInputStream s) 
  1851.     throws IOException, ClassNotFoundException {
  1852.     s.defaultReadObject();
  1853.  
  1854.     Vector          values = (Vector)s.readObject();
  1855.     int             indexCounter = 0;
  1856.     int             maxCounter = values.size();
  1857.  
  1858.     if(indexCounter < maxCounter && values.elementAt(indexCounter).
  1859.        equals("collapsedIcon")) {
  1860.         collapsedIcon = (Icon)values.elementAt(++indexCounter);
  1861.         indexCounter++;
  1862.     }
  1863.     if(indexCounter < maxCounter && values.elementAt(indexCounter).
  1864.        equals("expandedIcon")) {
  1865.         expandedIcon = (Icon)values.elementAt(++indexCounter);
  1866.         indexCounter++;
  1867.     }
  1868.     if(indexCounter < maxCounter && values.elementAt(indexCounter).
  1869.        equals("currentCellRenderer")) {
  1870.         currentCellRenderer = (TreeCellRenderer)values
  1871.                           .elementAt(++indexCounter);
  1872.         indexCounter++;
  1873.     }
  1874.     if(indexCounter < maxCounter && values.elementAt(indexCounter).
  1875.        equals("cellEditor")) {
  1876.         cellEditor = (TreeCellEditor)values.elementAt(++indexCounter);
  1877.         indexCounter++;
  1878.     }
  1879.     if(largeModel && tree != null && componentListener == null) {
  1880.         componentListener = new ComponentAdapter() {
  1881.         public void componentMoved(ComponentEvent e) {
  1882.             validCachedPreferredSize = false;
  1883.         }
  1884.         };
  1885.         tree.addComponentListener(componentListener);
  1886.     }
  1887.     }
  1888.  
  1889.     /** TreeTraverseAction is the action used for left/right keys.
  1890.       * Will toggle the expandedness of a node, as well as potentially
  1891.       * incrementing the selection.
  1892.       */
  1893.     private class TreeTraverseAction extends AbstractAction implements Serializable {
  1894.     /** Determines direction to traverse, 1 means expand, -1 means
  1895.       * collapse. */
  1896.     private int direction;
  1897.  
  1898.     public TreeTraverseAction(int direction, String name)
  1899.     {
  1900.         super(name);
  1901.         this.direction = direction;
  1902.     }
  1903.  
  1904.     public void actionPerformed(ActionEvent e)
  1905.     {
  1906.         int                rowCount;
  1907.  
  1908.         if(tree != null && (rowCount = getRowCount()) > 0)
  1909.         {
  1910.         int               minSelIndex = tree.getMinSelectionRow();
  1911.         int               newIndex;
  1912.  
  1913.         if(minSelIndex == -1)
  1914.             newIndex = 0;
  1915.         else {
  1916.             /* Try and expand the node, otherwise go to next
  1917.                node. */
  1918.             if(direction == 1) {
  1919.             if(!isLeaf(minSelIndex) && !isExpanded(minSelIndex)) {
  1920.                 expandRow(minSelIndex);
  1921.                 newIndex = -1;
  1922.             }
  1923.             else
  1924.                 newIndex = Math.min(minSelIndex + 1, rowCount - 1);
  1925.             }
  1926.             /* Try to collapse node. */
  1927.             else {
  1928.             if(!isLeaf(minSelIndex) && isExpanded(minSelIndex)) {
  1929.                 collapseRow(minSelIndex);
  1930.                 newIndex = -1;
  1931.             }
  1932.             else {
  1933.                 try
  1934.                 {
  1935.                 if(!largeModel)
  1936.                     newIndex =((VisibleTreeNode)
  1937.                            getNode(minSelIndex)
  1938.                            .getParent()).getRow();
  1939.                 else {
  1940.                     int[]          cIndex = new int[1];
  1941.                     LargeTreeModelNode   parent;
  1942.                 
  1943.                     parent = getLargeParentAndChildIndexOfRow
  1944.                          (minSelIndex, cIndex);
  1945.                     newIndex = parent.getRow();
  1946.                 }
  1947.                 }
  1948.                 catch (Exception exception) {
  1949.                 newIndex = -1;
  1950.                 }
  1951.             }
  1952.             }
  1953.         }
  1954.         if(newIndex != -1) {
  1955.             tree.setSelectionInterval(newIndex, newIndex);
  1956.             ensureRowsAreVisible(newIndex, newIndex);
  1957.         }
  1958.         }
  1959.     }
  1960.  
  1961.     public boolean isEnabled() { return (tree != null && tree.isEnabled()); }
  1962.     } // BasicTreeUI.TreeTraverseAction
  1963.  
  1964.  
  1965.     /** TreePageAction handles page up and page down events.
  1966.       */
  1967.     private class TreePageAction extends AbstractAction implements Serializable {
  1968.     /** Specifies the direction to adjust the selection by. */
  1969.     private int         direction;
  1970.  
  1971.     public TreePageAction(int direction, String name) {
  1972.         super(name);
  1973.         this.direction = direction;
  1974.     }
  1975.  
  1976.     public void actionPerformed(ActionEvent e) {
  1977.         int           rowCount;
  1978.  
  1979.         if(tree != null && (rowCount = getRowCount()) > 0 &&
  1980.         treeSelectionModel != null) {
  1981.         Dimension         maxSize = tree.getSize();
  1982.         int               newIndex;
  1983.         int               selIndex;
  1984.         Rectangle         selRect;
  1985.         Rectangle         visRect = tree.getVisibleRect();
  1986.         int               maxVisY = Math.max(0, maxSize.height -
  1987.                              visRect.height);
  1988.  
  1989.         if(direction == -1)
  1990.             selIndex = getMinSelectionRow();
  1991.         else
  1992.             selIndex = getMaxSelectionRow();
  1993.         if(selIndex != -1) {
  1994.             selRect = getRowBounds(selIndex);
  1995.             if(direction == -1) {
  1996.             /* Try to scroll the currently selected row to
  1997.                the bottom of the screen. */
  1998.             visRect.y = Math.max(0, selRect.y + selRect.height -
  1999.                          visRect.height);
  2000.             }
  2001.             else {
  2002.             /* Try to scroll the currently selected row to
  2003.                the top of the screen. */
  2004.             visRect.y = Math.min(maxVisY, selRect.y);
  2005.             }
  2006.         }
  2007.         else {
  2008.             /* Scroll a whole page size. */
  2009.             visRect.y = Math.min(maxVisY, visRect.y + visRect.height *
  2010.             direction);
  2011.         }
  2012.         if(direction == 1) {
  2013.             newIndex = getClosestRowForLocation(visRect.x, visRect.y +
  2014.                             visRect.height - 1);
  2015.             selRect = getRowBounds(newIndex);
  2016.             visRect.y = Math.min(maxVisY, (selRect.y + selRect.height)-
  2017.                      visRect.height);
  2018.         }
  2019.         else {
  2020.             newIndex = getClosestRowForLocation(visRect.x,
  2021.                             visRect.y + 1);
  2022.             selRect = getRowBounds(newIndex);
  2023.             visRect.y = Math.min(maxVisY, selRect.y);
  2024.         }
  2025.         tree.scrollRectToVisible(visRect);
  2026.         tree.setSelectionRow(newIndex);
  2027.         }
  2028.     }
  2029.  
  2030.     public boolean isEnabled() { return (tree != null && tree.isEnabled()); }
  2031.  
  2032.     } // BasicTreeUI.TreePageAction
  2033.  
  2034.  
  2035.     /** TreeIncrementAction is used to handle up/down actions.  Selection
  2036.       * is moved up or down based on direction.
  2037.       */
  2038.     private class TreeIncrementAction extends AbstractAction implements
  2039.         Serializable {
  2040.     /** Specifies the direction to adjust the selection by. */
  2041.     private int         direction;
  2042.  
  2043.     public TreeIncrementAction(int direction, String name)
  2044.     {
  2045.         super(name);
  2046.         this.direction = direction;
  2047.     }
  2048.  
  2049.     public void actionPerformed(ActionEvent e)
  2050.     {
  2051.         int              rowCount;
  2052.  
  2053.         if(tree != null && treeSelectionModel != null &&
  2054.         (rowCount = getRowCount()) > 0) {
  2055.         int                  minSelIndex = tree.getMaxSelectionRow();
  2056.         int                  newIndex;
  2057.  
  2058.         if(minSelIndex == -1) {
  2059.             if(direction == 1)
  2060.             newIndex = 0;
  2061.             else
  2062.             newIndex = rowCount - 1;
  2063.         }
  2064.         else
  2065.             /* Aparently people don't like wrapping;( */
  2066.             newIndex = Math.min(rowCount - 1, Math.max
  2067.                     (0, (minSelIndex + direction)));
  2068.         tree.setSelectionInterval(newIndex, newIndex);
  2069.         ensureRowsAreVisible(newIndex, newIndex);
  2070.         }
  2071.     }
  2072.  
  2073.     public boolean isEnabled() { return (tree != null && tree.isEnabled()); }
  2074.  
  2075.     } // End of class BasicTreeUI.TreeIncrementAction
  2076.  
  2077.     /**
  2078.       * TreeHomeAction is used to handle end/home actions.
  2079.       * Scrolls either the first or last cell to be visible based on
  2080.       * direction.
  2081.       */
  2082.     private class TreeHomeAction extends AbstractAction implements
  2083.             Serializable {
  2084.     private int            direction;
  2085.  
  2086.     public TreeHomeAction(int direction, String name) {
  2087.         super(name);
  2088.         this.direction = direction;
  2089.     }
  2090.  
  2091.     public void actionPerformed(ActionEvent e) {
  2092.         int                   rowCount = getRowCount();
  2093.  
  2094.         if(tree != null && rowCount > 0) {
  2095.         if(direction == -1) {
  2096.             ensureRowsAreVisible(0, 0);
  2097.             tree.setSelectionInterval(0, 0);
  2098.         }
  2099.         else {
  2100.             ensureRowsAreVisible(rowCount - 1, rowCount - 1);
  2101.             tree.setSelectionInterval(rowCount - 1, rowCount - 1);
  2102.         }
  2103.         }
  2104.     }
  2105.  
  2106.     public boolean isEnabled() { return (tree != null && tree.isEnabled()); }
  2107.  
  2108.     } // End of class BasicTreeUI.TreeHomeAction
  2109.  
  2110.  
  2111.     /**
  2112.       * For the first selected row expandedness will be toggled.
  2113.       */
  2114.     private class TreeToggleAction extends AbstractAction implements
  2115.                 Serializable {
  2116.     public TreeToggleAction(String name) {
  2117.         super(name);
  2118.     }
  2119.  
  2120.     public void actionPerformed(ActionEvent e) {
  2121.         if(tree != null) {
  2122.         int            selRow = tree.getMinSelectionRow();
  2123.  
  2124.         if(selRow != -1 && !isLeaf(selRow)) {
  2125.             if(isExpanded(selRow))
  2126.             collapseRow(selRow);
  2127.             else
  2128.             expandRow(selRow);
  2129.         }
  2130.         }
  2131.     }
  2132.  
  2133.     public boolean isEnabled() { return (tree != null && tree.isEnabled()); }
  2134.  
  2135.     } // End of class BasicTreeUI.TreeToggleAction
  2136.  
  2137.  
  2138.     /**
  2139.       * BasicTreeMouseListener handles passing all mouse events,
  2140.       * including mouse motion events, until the mouse is released to
  2141.       * the destination it is constructed with. It is assumed all the
  2142.       * events are currently target at source.
  2143.       */
  2144.     // PENDING(scott): this could actually be moved into a general
  2145.     // location, no reason to be in here.
  2146.     public class BasicTreeMouseListener extends Object implements
  2147.            MouseListener, MouseMotionListener, Serializable
  2148.     {
  2149.     /** Source that events are coming from. */
  2150.     protected Component        source;
  2151.     /** Destination that recieves all events. */
  2152.     protected Component        destination;
  2153.  
  2154.     public BasicTreeMouseListener(Component source, Component destination,
  2155.                                   MouseEvent event){
  2156.         this.source = source;
  2157.         this.destination = destination;
  2158.         this.source.addMouseListener(this);
  2159.         this.source.addMouseMotionListener(this);
  2160.         /* Dispatch the editing event! */
  2161.         destination.dispatchEvent(SwingUtilities.convertMouseEvent
  2162.                       (source, event, destination));
  2163.     }
  2164.  
  2165.     public void mouseClicked(MouseEvent e) {
  2166.         if(destination != null)
  2167.         destination.dispatchEvent(SwingUtilities.convertMouseEvent
  2168.                       (source, e, destination));
  2169.     }
  2170.  
  2171.     public void mousePressed(MouseEvent e) {
  2172.     }
  2173.  
  2174.     public void mouseReleased(MouseEvent e) {
  2175.         if(destination != null)
  2176.         destination.dispatchEvent(SwingUtilities.convertMouseEvent
  2177.                       (source, e, destination));
  2178.         removeFromSource();
  2179.     }
  2180.  
  2181.     public void mouseEntered(MouseEvent e) {
  2182.         removeFromSource();
  2183.     }
  2184.     
  2185.     public void mouseExited(MouseEvent e) {
  2186.         removeFromSource();
  2187.     }
  2188.  
  2189.     public void mouseDragged(MouseEvent e) {
  2190.         removeFromSource();
  2191.     }
  2192.  
  2193.     public void mouseMoved(MouseEvent e) {
  2194.         removeFromSource();
  2195.     }
  2196.  
  2197.     protected void removeFromSource() {
  2198.         if(source != null) {
  2199.         source.removeMouseListener(this);
  2200.         source.removeMouseMotionListener(this);
  2201.         }
  2202.         source = destination = null;
  2203.     }
  2204.  
  2205.     } // End of class BasicTreeUI.BasicTreeMouseListener
  2206.  
  2207.     /**
  2208.      * BasicTreeUIPaintInfo is used during a painting session for largeModels.
  2209.      */
  2210.     public static class BasicTreeUIPaintInfo {
  2211.     public Graphics                g;
  2212.     public TreeCellRenderer        renderer;
  2213.     public int                     totalIndent;
  2214.     public Color                   hashColor;
  2215.     public int                     halfRowHeight;
  2216.     public int                     levelOffset;
  2217.     public Icon                    expandedIcon;
  2218.     public Icon                    collapsedIcon;
  2219.     public int                     rChildIndent;
  2220.     public int                     rowHeight;
  2221.     public JTree                   tree;
  2222.     public TreeModel               treeModel;
  2223.     public int                     leadIndex;
  2224.  
  2225.     public BasicTreeUIPaintInfo(BasicTreeUI ui, Graphics g) {
  2226.         reset(ui, g);
  2227.     }
  2228.  
  2229.     public void reset(BasicTreeUI ui, Graphics g) {
  2230.         this.g = g;
  2231.         this.renderer = ui.getCellRenderer();
  2232.         this.totalIndent = ui.totalChildIndent;
  2233.         this.hashColor = ui.getHashColor();
  2234.         this.halfRowHeight = ui.getRowHeight() / 2;
  2235.         if(ui.getShowsRootHandles())
  2236.         this.levelOffset = 1;
  2237.         else
  2238.         this.levelOffset = 0;
  2239.         this.expandedIcon = ui.getExpandedIcon();
  2240.         this.collapsedIcon = ui.getCollapsedIcon();
  2241.         this.rChildIndent = ui.getRightChildIndent();
  2242.         this.rowHeight = ui.getRowHeight();
  2243.         this.tree = ui.tree;
  2244.         this.treeModel = ui.getModel();
  2245.         if(!ui.tree.hasFocus())
  2246.         this.leadIndex = -1;
  2247.         else
  2248.         this.leadIndex = ui.tree.getLeadSelectionRow();
  2249.     }
  2250.     } // End of class BasicTreeUI.BasicTreeUIPaintInfo
  2251.  
  2252. } // End of class BasicTreeUI
  2253.